# -*- coding: utf-8 -*-

from django import forms
from django.forms.util import ErrorList
from djtools.formfields import BrPlacaVeicularField, IntegerField, \
    AlphaNumericUpperCaseField, NumericField, CapitalizeTextField, BrDinheiroField, BrDataHoraField
from djtools.formwidgets import BrDataHoraWidget
from datetime import datetime
from djtools import db
from decimal import Decimal

from comum.models import Pessoa
from frota.enums import Situacao
from frota.models import Veiculo, VeiculoModelo, VeiculoMarca, VeiculoCor, VeiculoCombustivel
from frota.models import Viatura, ViaturaOrdemAbastecimento, ViaturaStatus, Viagem, ViagemAgendamento, ViagemAgendamentoResposta

######################
### METODOS COMUNS ###
######################

#def viaturas_as_choices(): # retorna um choice onde o grupo é utilizado para categorizar as viaturas
#    grupos_viaturas = []
#    for grupo_viatura in ViaturaGrupo.objects.all():
#        viaturas = []
#        for viatura in Viatura.objects.filter(grupo=grupo_viatura.id):
#            viaturas.append([viatura.id,viatura]) 
#        
#        if len(viaturas) != 0:
#            grupos_viaturas.append([grupo_viatura.nome,viaturas])
#    return grupos_viaturas

### retorna um choice cotendo as viaturas dísponiveis para um determinado grupo ###
def viaturas_disponiveis_as_choices(data_inicio, data_limite, tipo_viatura, empty_select=True):
    # é preciso considerar tanto as viaturas que estão em trânsito, devido a uma viagem iniciada quanto as viaturas 
    # presentes nos agendamentos, já que é permitido alterar a viatura que estava reservada para a viagem
    strConsulta = """select r.id
                           from frota_viatura v, frota_viagemagendamento a, frota_viagemagendamentoresposta r, frota_viagem g
                           where a.id=r.agendamento_id and
                                 r.id = g.agendamento_resposta_id and
                                 ((a.data_saida >= '%s' and a.data_saida <= '%s') or
                                 (a.data_chegada >= '%s' and a.data_chegada <= '%s') or
                                 ('%s' >= a.data_saida and '%s' <= a.data_chegada)) and
                                 v.tipo_id=%d""" %(data_inicio, data_limite, data_inicio, data_limite, data_inicio, data_limite, tipo_viatura)
                             
    viaturas_viagens = db.get_list(strConsulta)
    
    # respostas para os agendamentos
    agend_viagens = ''
    for i, consulta in enumerate(viaturas_viagens):
        if i == 0:
            agend_viagens += str(consulta[0])
        else:
            agend_viagens += ',' + str(consulta[0])

    # viaturas presentes nas viagens para o intervalo de datas
    ids_viaturas = [consulta[1] for consulta in viaturas_viagens]
    
    strConsulta = """select v.veiculo_ptr_id
                           from frota_viatura v, frota_viagemagendamento a, frota_viagemagendamentoresposta r
                           where a.id=r.agendamento_id and
                                 ((a.data_saida >= '%s' and a.data_saida <= '%s') or
                                 (a.data_chegada >= '%s' and a.data_chegada <= '%s') or
                                 ('%s' >= a.data_saida and '%s' <= a.data_chegada)) and
                                 v.tipo_id=%d""" %(data_inicio, data_limite, data_inicio, data_limite, data_inicio, data_limite, tipo_viatura)
    
    # adiciona os ids dos agendamentos ao filtro se necessário
    if viaturas_viagens:
        strConsulta += ' and r.id not in (%s) ' %(agend_viagens)
    
    viaturas = []
    if empty_select:
        viaturas.append(['', '---------'])
    
    # viaturas presentes nas respostas dos agendamentos que ainda não estão associadas a uma viagem
    viaturas_resp_agend = db.get_list(strConsulta)
    for viatura in viaturas_resp_agend:
        ids_viaturas.append(viatura)
    
    for viatura in Viatura.objects.filter(tipo=tipo_viatura).exclude(id__in=ids_viaturas):
        viaturas.append([viatura.id,viatura]) 
        
    return viaturas

### retorna um choice cotendo as viaturas dísponiveis para um determinado grupo ###
def viaturas_disponiveis_viagem_as_choices(data_inicio, data_limite, grupo_viatura, viatura): 
    viaturas = viaturas_disponiveis_as_choices(data_inicio, data_limite, grupo_viatura, False)
    viaturas.append([viatura.id, viatura])
    return viaturas

### retorna um choice onde o status é utilizado ###
def status_as_choices():
    statuss = []
    for status in Situacao.get_choices():
        if status != Situacao.PENDENTE:
            statuss.append([status[0],status[1]]) 
    
    return statuss

## retornar um choice de pessoas físicas que possuem a função de motorista
def motoristas_disponiveis_as_choices():    
    motoristas = []
    motoristas.append(['', '---------'])
    for motorista in Pessoa.objects.all():
        motoristas.append([motorista.id, motorista])
    
    return motoristas

###############
### VEICULO ###
###############

class VeiculoModeloForm(forms.ModelForm):
    nome = CapitalizeTextField(label=u'Nome', max_length=20)
    class Meta:
        model = VeiculoModelo

class VeiculoCorForm(forms.ModelForm):
    nome = CapitalizeTextField(label=u'Nome', max_length=20)
    class Meta:
        model = VeiculoCor
        
class VeiculoCombustivelForm(forms.ModelForm):
    nome = CapitalizeTextField(label=u'Nome', max_length=20)
    class Meta:
        model = VeiculoCombustivel

class VeiculoMarcaForm(forms.ModelForm):
    nome = CapitalizeTextField(label=u'Nome', max_length=20)
    class Meta:
        model = VeiculoMarca

class VeiculoForm(forms.ModelForm):
    ano_fabric = IntegerField(label=u'Ano', help_text='Ano de Fabricação, ex: 2010', max_length=4)
    placa = BrPlacaVeicularField(label=u'Placa', help_text=u'ex: NAT-1010')
    
    class Meta:
        model = Veiculo
    
###############
### VIATURA ###
###############

class ViaturaForm(VeiculoForm):
    combustiveis = forms.ModelMultipleChoiceField(label=u'Combustíveis', widget=forms.CheckboxSelectMultiple, queryset=VeiculoCombustivel.objects.all())

    placa = BrPlacaVeicularField(label=u'Placa', help_text=u'ex: NAT-1010')
    
    lotacao = IntegerField(label=u'Lotação', help_text=u'Capacidade máxima de ocupantes incluindo o motorista', max_length=2)
    odometro = IntegerField(label=u'Odômetro', help_text=u'Km rodados', max_length=6)
    
    chassi = AlphaNumericUpperCaseField(label=u'Chassi', max_length=17, min_length=17)
    renavam = NumericField(label=u'Renavam', max_length=9, min_length=9)
    
    potencia = IntegerField(label=u'Potência', help_text=u'Potência em HP', max_length=3, required=False)
    cilindrada = IntegerField(label=u'Cilindrada', help_text=u'Cilindrada em CC', max_length=3, required=False)
    consumo_medio = IntegerField(label=u'Consumo Médio', help_text=u'Consumo (Km/l)', required=False)
    
    capacidade_tanque = IntegerField(label=u'Tanque', help_text=u'Capacidade (l)', max_length=3)
    capacidade_gnv = IntegerField(label=u'Cilindro', help_text=u'Capacidade (m³)', max_length=3, required=False)
    
    status = forms.ModelChoiceField(label=u'Situação', widget=forms.Select(), queryset=ViaturaStatus.objects.all(), initial=1, required=False)
    
    class Meta:
        model = Viatura
    
    def clean_lotacao(self):
        if 'lotacao' in self.cleaned_data:
            if self.cleaned_data['lotacao'] < 1:
                self._errors['lotacao'] = ErrorList([u'O veículo deve possuir no mínimo um ocupante.'])
        return self.cleaned_data['lotacao']
    
    # utilizado para verificar se existem registros na tabela de status de viaturas
    def clean_status(self):
        if 'status' in self.cleaned_data:
            situacao = ViaturaStatus.objects.filter(descricao=u'Disponível')
            if not len(situacao):
                self._errors['__all__'] = ErrorList([u'O registro que indica a disponibilidade da viatura não foi encontrado.'])
            else:
                self.instance.status = ViaturaStatus.objects.get(descricao=u'Disponível')
        return self.cleaned_data['status']
       
    def clean(self):
        combLiq = False # indica se foi selecionado algum combustível além do gnv
        combGnv = False # indica se foi seleciona gás natural como combustível
        if 'combustiveis' in self.cleaned_data:
            for combustivel in self.cleaned_data['combustiveis']:
                if combustivel.nome == u'Gás Natural':
                    combGnv = True
                    if self.cleaned_data['capacidade_gnv'] is None:
                        self._errors['capacidade_gnv'] = ErrorList([u'Este campo é obrigatório se o combustível gás natural for selecionado.'])
                else:
                    combLiq = True
                    
            if combLiq == False:
                self._errors['combustiveis'] = ErrorList([u'Selecione também um combustível líquido.'])
                
        if combGnv == False and self.cleaned_data['capacidade_gnv'] is not None:
            self._errors['capacidade_gnv'] = ErrorList([u'Selecione o combustível gás natural quando fornecer um valor para a capacidade do cilindro.'])
                
        return self.cleaned_data
    
class ViagemAgendamentoForm(forms.ModelForm):
#    solicitante = forms.ModelChoiceField(queryset=Pessoa.objects.all(), widget=AutocompleteWidget(extraParams={'force_generic_search': '1'}, minChars=5))
    data_saida = BrDataHoraField(label=u'Duração', help_text=u'Data de início')
    data_chegada = BrDataHoraField(help_text=u'Data limite', widget=BrDataHoraWidget(show_label=False))
    
    class Meta:
        model = ViagemAgendamento

    def clean_data_saida(self):
        if 'data_saida' in self.cleaned_data:
            if self.cleaned_data['data_saida'] <= datetime.now():
                self._errors['data_saida'] = ErrorList([u'A data de saída não pode ser inferior a data atual.'])
        return self.cleaned_data['data_saida']

    def clean_data_chegada(self):
        if 'data_chegada' in self.cleaned_data:
            if self.cleaned_data['data_chegada'] <= self.cleaned_data['data_saida']:
                self._errors['data_chegada'] = ErrorList([u'A data de chegada não pode ser inferior a data de saída.'])
        return self.cleaned_data['data_chegada']
    
class ViagemAgendamentoRespostaForm(forms.ModelForm):
    agendamento = forms.ModelChoiceField(queryset=ViagemAgendamento.objects.all(), widget=forms.HiddenInput())
    status = forms.CharField(widget=forms.Select(choices=status_as_choices()), required=True)
    viatura = forms.ModelChoiceField(queryset=Viatura.objects.all(), widget=forms.Select(), required=False)
    TITLE = u'Avaliação de Agendamento'
    
    fieldsets = (
        (None, {
            'fields': ('agendamento',
                       'status',
                       'viatura',
                       'obs')
        }),
    )
    
    class Meta:
        model = ViagemAgendamentoResposta
        fields = ['status', 'viatura', 'obs']
    

    def __init__(self, *args, **kwargs):
        agendamento_id = kwargs.pop('agendamento_id')
        agendamento = ViagemAgendamento.objects.get(id=agendamento_id)
        super(ViagemAgendamentoRespostaForm, self).__init__(*args, **kwargs)
        
        self.fields['agendamento'].initial = agendamento.id
        self.fields['viatura'].widget.choices = viaturas_disponiveis_as_choices(agendamento.data_saida, \
                                                                                agendamento.data_chegada, \
                                                                                agendamento.tipo_viatura.id)
        self.fields['obs'].widget.attrs['cols'] = '100'
                
        if agendamento.data_saida < datetime.now(): #indica que a avaliação está sendo realizada depois após o horário agendado para a viagem
            self.fields['status'].widget.choices = [[Situacao.INDEFERIDA,Situacao.INDEFERIDA]] #permite que apenas a opção indeferida seja listada
            self.fields['obs'].initial = 'Não foi possível realizar a avaliação antes da data agendada para o início da viagem'
            return
            
        if len(self.fields['viatura'].widget.choices) <= 1: #indica que existe apenas a opção None='---------', sem viaturas disponíveis
            self.fields['status'].widget.choices = [[Situacao.INDEFERIDA,Situacao.INDEFERIDA]] #permite que apenas a opção indeferida seja listada
            self.fields['obs'].initial = 'Não existe viatura disponível para o período informado'      

    def clean(self): 
        if 'status' in self.cleaned_data and 'viatura' in self.cleaned_data:
            if self.cleaned_data['viatura'] is None and self.data['status'] == Situacao.DEFERIDA: #indica que a solicitação foi deferida
                self._errors['viatura'] = ErrorList([u'Indique a viatura reservada para a viagem.'])
        return self.cleaned_data

    def save(self):
        self.instance.agendamento = ViagemAgendamento.objects.get(id=self.data['agendamento'])
        
        if self.data['status'] and self.data['status'] in Situacao.get_lista_simples():
            status = self.data['status']
            self.instance.agendamento.status = status
            self.instance.agendamento.save()
            
            if status == Situacao.DEFERIDA:
                if self.data['viatura']:
                    self.instance.viatura = Viatura.objects.get(id=self.data['viatura'])
        else:
            self.instance.agendamento.status = Situacao.INDEFERIDA
                   
        self.instance.data = datetime.now()
              
        self.instance.obs = self.data['obs']
        self.instance.save()
        return self.instance
    
class ViagemSaidaForm(forms.ModelForm):
    #queryset utilizado em por mais de um Field
    querysetP = Pessoa.objects.all()
    
    agendamento_resposta = forms.ModelChoiceField(queryset=ViagemAgendamentoResposta.objects.all(), widget=forms.HiddenInput())
    viatura = forms.ModelChoiceField(queryset=Viatura.objects.all(), widget=forms.Select())
    motorista = forms.ModelChoiceField(queryset=querysetP, widget=forms.Select())
    saida_data = BrDataHoraField(label=u'Data e Hora')
    saida_odometro = IntegerField(label=u'Odomêtro', help_text=u'Km rodados', max_length=6, widget=forms.TextInput(attrs={'class':'integer-widget','size':'11', 'readonly': 'readonly'}))
    

    fieldsets = (
        (None, {
            'fields': ('agendamento_resposta',
                       'saida_data', 'viatura',
                       'motorista', 'saida_odometro',
                       'saida_obs')
        }),
    )    
    

    TITLE = u'Registrar Início de Viagem'
    
    class Meta:
        model = Viagem
         
    def __init__(self, *args, **kwargs):
        resp_agendamento_id = kwargs.pop('resp_agendamento_id')
        resp_agendamento = ViagemAgendamentoResposta.objects.get(id=resp_agendamento_id)
        super(ViagemSaidaForm, self).__init__(*args, **kwargs)
        self.fields['viatura'].widget.choices = viaturas_disponiveis_viagem_as_choices(resp_agendamento.agendamento.data_saida, \
                                                                                resp_agendamento.agendamento.data_chegada, \
                                                                                resp_agendamento.agendamento.tipo_viatura.id, \
                                                                                resp_agendamento.viatura)
        self.fields['viatura'].initial = resp_agendamento.viatura.id
        self.fields['motorista'].widget.choices = motoristas_disponiveis_as_choices()
        self.fields['agendamento_resposta'].initial = resp_agendamento.id
        self.fields['saida_data'].initial = datetime.now()
        self.fields['saida_odometro'].initial = resp_agendamento.viatura.odometro
        self.fields['saida_obs'].widget.attrs['cols'] = '100'
    
    def save(self):
        viagem = super(ViagemSaidaForm, self).save()
        viagem.viatura.status = ViaturaStatus.objects.get(descricao='Em Trânsito')
        viagem.viatura.save()
        return viagem
    
class ViagemChegadaForm(forms.ModelForm):
    chegada_data = BrDataHoraField(label=u'Data e Hora')
    chegada_odometro = IntegerField(label=u'Odomêtro', help_text=u'Km rodados', max_length=6, widget=forms.TextInput(attrs={'class':'integer-widget','size':'11'}))
    TITLE = u'Registrar Término de Viagem'
    
    class Meta:
        model = Viagem
        fields = ['chegada_data', 'chegada_odometro', 'chegada_obs']
    
    def __init__(self, *args, **kwargs):
        super(ViagemChegadaForm, self).__init__(*args, **kwargs)
        self.fields['chegada_data'].initial = datetime.now()
        self.fields['chegada_obs'].widget.attrs['cols'] = '100'
    
    def clean_chegada_data(self):
        if 'chegada_data' in self.cleaned_data:
            if self.cleaned_data['chegada_data'] < self.instance.saida_data:
                self._errors['chegada_data'] = ErrorList([u'A data de término da viagem não pode ser inferior a data de saída.'])
        return self.cleaned_data['chegada_data']
    
    def clean_chegada_odometro(self):
        if 'chegada_odometro' in self.cleaned_data:
            if int(self.cleaned_data['chegada_odometro']) < self.instance.agendamento_resposta.viatura.odometro:
                self._errors['chegada_odometro'] = ErrorList([u'O odômetro não pode ser inferior ao cadastrado para a viatura.'])
        return self.cleaned_data['chegada_odometro']
        
    def save(self, force_insert=False, force_update=False, commit=True):
        viagem = super(ViagemChegadaForm, self).save(commit=False)
        if commit:
            viagem.viatura.status = ViaturaStatus.objects.get(descricao='Disponível')
            viagem.viatura.odometro = self.data['chegada_odometro']
            viagem.viatura.save()
            viagem.save()
        return viagem
    
class ViaturaOrdemAbastecimentoForm(forms.ModelForm):
    viagem = forms.ModelChoiceField(queryset=Viagem.objects.all(), widget=forms.HiddenInput())
    data = BrDataHoraField()
    cupom_fiscal = NumericField(label=u'Cupom Fiscal', help_text=u'Número do cupom fiscal', max_length=10)
    combustivel = forms.ModelChoiceField(label=u'Combustível',queryset=VeiculoCombustivel.objects.all())
    quantidade = BrDinheiroField(help_text=u'Quantidade de litros ou de metros cúbicos utilizada no abastecimento')
    valor_unidade = BrDinheiroField(label=u'Valor Unitário', help_text=u'Valor do litro ou do metro cúbico utilizado no abastecimento')
    oleo = CapitalizeTextField(label=u'Óleo', help_text='Especificação do óleo lubricante utilizado', max_length=20, required=False)
    valor_oleo = BrDinheiroField(label=u'Valor do Óleo', help_text='Valor total do óleo utilizado', required=False)
    
    TITLE = u'Registrar Ordem de Abastecimento'
    
    class Meta:
        model = ViaturaOrdemAbastecimento
    
    def __init__(self, *args, **kwargs):
        viagem_id = kwargs.pop('viagem_id')
        viagem = Viagem.objects.get(id=viagem_id)
        super(ViaturaOrdemAbastecimentoForm, self).__init__(*args, **kwargs)
        self.fields['viagem'].initial = viagem.id
    
    def clean_data(self):
        if 'data' in self.cleaned_data:
            if self.cleaned_data['data'] <= self.cleaned_data['viagem'].saida_data or self.cleaned_data['data'] >= self.cleaned_data['viagem'].chegada_data:
                self._errors['data'] = ErrorList([u'A data do abastecimento deve coincidir com a data de saída e chegada da viatura.'])
        return self.cleaned_data['data']
    
    def clean_cupom_fiscal(self):
        if 'cupom_fiscal' in self.cleaned_data:
            ordemAbastecimento = ViaturaOrdemAbastecimento.objects.filter(cupom_fiscal=self.cleaned_data['cupom_fiscal'])
            if len(ordemAbastecimento): # indica que existe algum registro no banco com o cupom fiscal indicado
                if self.instance.pk is None: # indica que o fomulário está sendo utilizado para cadastro e não edição 
                    self._errors['cupom_fiscal'] = ErrorList([u'Já existe uma ordem de abastecimento cadastrada com este número de cupom fiscal.'])
        return self.cleaned_data['cupom_fiscal']
    
    def clean_combustivel(self):
        if 'combustivel' in self.cleaned_data:
            combustivel = Viatura.objects.get(id=self.cleaned_data['viagem'].agendamento_resposta.viatura.id).combustiveis.filter(id=self.cleaned_data['combustivel'].id)
            if len(combustivel) == 0: # indica que não existe registro no banco com essa viatura e esse combustível
                self._errors['combustivel'] = ErrorList([u'A viatura utilizada na viagem não pode utilizar este combustível.'])
        return self.cleaned_data['combustivel']
    
    def clean(self):
        if 'quantidade' in self.cleaned_data:
            if self.cleaned_data['quantidade'] == Decimal('0.00'):
                self._errors['quantidade'] = ErrorList([u'Indique um valor diferente de "0,00".'])
            capacidade_tanque = Viatura.objects.get(id=self.cleaned_data['viagem'].agendamento_resposta.viatura.id).capacidade_tanque
            if self.cleaned_data['quantidade'] > capacidade_tanque:
                self._errors['quantidade'] = ErrorList([u'Valor não pode ser maior que a capacidade do tanque.'])
    
        if 'valor_unidade' in self.cleaned_data:
            if self.cleaned_data['valor_unidade'] == Decimal('0.00'):
                self._errors['valor_unidade'] = ErrorList([u'Indique um valor diferente de "0,00".'])
        
        if 'oleo' in self.cleaned_data:
            if self.cleaned_data['oleo'] != u'' and (self.cleaned_data['valor_oleo'] is None or self.cleaned_data['valor_oleo'] == Decimal('0.00')):
                if self.cleaned_data['valor_oleo'] == Decimal('0.00'):
                    self._errors['valor_oleo'] = ErrorList([u'Indique um valor diferente de "0,00".'])
                else:
                    self._errors['valor_oleo'] = ErrorList([u'Indique o valor gasto com o óleo.'])
            if self.cleaned_data['oleo'] == u'' and (self.cleaned_data['valor_oleo'] is not None or self.cleaned_data['valor_oleo'] == Decimal('0.00')):
                self._errors['oleo'] = ErrorList([u'Indique a especificação do óleo.']) 
       
        return self.cleaned_data